﻿// Copyright (c) 2012 - 2021 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2021-03-19 version 4.0.0 Jared Zheng <jaredz at outlook dot com>

#include "stdafx.h"
#include "ServerConfig.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CServerConfig : 可以将对应文字描述改成本地语言
CPCXStr CServerConfig::ServerConfig    = TF("ServerConfig");
CPCXStr CServerConfig::NetworkPtr      = TF("NetworkPtr");
                                       
CPCXStr CServerConfig::ConfigName      = TF("ServerConfig.cfg");
CPCXStr CServerConfig::ConfigServer    = TF("ConfigServer");
                                       
CPCXStr CServerConfig::NetworkConfig   = TF("NetworkConfig");
CPCXStr CServerConfig::NetworkAttr     = TF("NetworkAttr");
CPCXStr CServerConfig::NetworkThread   = TF("NetworkThread");
CPCXStr CServerConfig::NetworkAck      = TF("NetworkAck");
CPCXStr CServerConfig::NetworkTimeout  = TF("NetworkTimeout");
CPCXStr CServerConfig::NetworkBuffer   = TF("NetworkBuffer");
CPCXStr CServerConfig::NetworkJumbo    = TF("NetworkJumbo");
                                       
CPCXStr CServerConfig::CommonConfig    = TF("CommonConfig");
CPCXStr CServerConfig::FrameLogLevel   = TF("FrameLogLevel");
CPCXStr CServerConfig::MgrLogLevel     = TF("MgrLogLevel");
CPCXStr CServerConfig::UpdateTick      = TF("UpdateTick");
CPCXStr CServerConfig::PingEnable      = TF("PingEnable");
CPCXStr CServerConfig::AnyListen       = TF("AnyListen");
CPCXStr CServerConfig::FramePath       = TF("FramePath");
CPCXStr CServerConfig::LoadServers     = TF("LoadServers");

CPCXStr CServerConfig::MasterServer    = TF("MasterServer");
CPCXStr CServerConfig::SlaveServer     = TF("SlaveServer");
CPCXStr CServerConfig::NodeServer      = TF("NodeServer");

CPCXStr CServerConfig::Frame           = TF("Frame");
CPCXStr CServerConfig::Rank            = TF("Rank");

CPCXStr CServerConfig::Value           = TF("Value");
CPCXStr CServerConfig::Name            = TF("Name");
CPCXStr CServerConfig::Sync            = TF("Sync");
CPCXStr CServerConfig::Ip              = TF("Ip");
CPCXStr CServerConfig::Port            = TF("Port");
CPCXStr CServerConfig::Id              = TF("Id");
CPCXStr CServerConfig::Queue           = TF("Queue");
CPCXStr CServerConfig::Load            = TF("Load");
CPCXStr CServerConfig::Rate            = TF("Rate");

CPCXStr CServerConfig::Listen          = TF("Listen");
CPCXStr CServerConfig::ListenIp        = TF("ListenIp");
CPCXStr CServerConfig::ListenPort      = TF("ListenPort");
CPCXStr CServerConfig::Connect         = TF("Connect");
CPCXStr CServerConfig::Routine         = TF("Routine");
CPCXStr CServerConfig::Inst            = TF("Inst");
CPCXStr CServerConfig::Module          = TF("Module");

CPCXStr CServerConfig::LocalHost       = TF("localhost");
CPCXStr CServerConfig::AddrAnyV4       = TF("0.0.0.0");
CPCXStr CServerConfig::AddrAnyV6       = TF("::");
CPCXStr CServerConfig::RoutineFolder   = TF("Routine");
CPCXStr CServerConfig::LogFolder       = TF("Log");
CPCXStr CServerConfig::LibPrefix       = TF("lib");
#ifdef __RUNTIME_DEBUG__
    #if (__PLATFORM_TARGET__ == PLATFORM_TARGET_WINDOWS)
        CPCXStr CServerConfig::LibExt             = TF("Debug.dll");
        XChar   CServerConfig::PathSepChar         = TF('\\');
    #elif (__PLATFORM_TARGET__ == PLATFORM_TARGET_LINUX)
        CPCXStr CServerConfig::LibExt             = TF("Debug.so");
        XChar   CServerConfig::PathSepChar         = TF('/');
    #endif
#else
    #if (__PLATFORM_TARGET__ == PLATFORM_TARGET_WINDOWS)
        CPCXStr CServerConfig::LibExt             = TF(".dll");
        XChar   CServerConfig::PathSepChar         = TF('\\');
    #elif (__PLATFORM_TARGET__ == PLATFORM_TARGET_LINUX)
        CPCXStr CServerConfig::LibExt             = TF(".so");
        XChar   CServerConfig::PathSepChar         = TF('/');
    #endif
#endif

// -ServerConfig:test.txt
//----------------------------------
// -NetworkAttr:32(ipv4)/64(ipv6), ATTR_NETORDER不支持
// -NetworkThread:1
// -NetworkAck:8000
// -NetworkTimeout:80000
// -NetworkBuffer:8000
// -NetworkJumbo:64000
//----------------------------------
// -FrameLogLevel
// -MgrLogLevel
// -UpdateTick
// -PingEnable:
// -AnyListen:
// -FramePath:
// -Routine:
// -Log:
// -LoadServers:
//----------------------------------
// -ConfigServer0---N:COMMON_TYPE_ENUM(开启每个编号的命令行配置)
// -Name0---N:
// -Sync0---N:
// -Id0---N:
// -Queue0---N:
// -Load0---N:
// -Rate0---N:
// -Listen0---N:Port,Ip
// -Connect0---N:3,ListenPort,ListenIp[,Port,Ip][;5,ListenPort,ListenIp,Port,Ip];
// -Routine0---N:
// -Inst0---N:
// -Module0---N:
void CServerConfig::SetArgs(PCXStr pszCmd)
{
    m_cmdArgv.RemoveAll();

    CString strKey;
    CString strValue;

    PXStr pszToken = nullptr;
    PXStr pszArg   = CXChar::Tok((PXStr)pszCmd, TF(" "), pszToken);
    while (pszArg != nullptr)
    {
        if (pszArg[0] == TF('-'))
        {
            ++pszArg;
            PCXStr pszSep = CXChar::Chr(pszArg, TF(':'));
            if (pszSep != nullptr)
            {
                strKey.FillBuffer(pszArg, (pszSep - pszArg));
                strValue = (pszSep + 1);

                DEV_DEBUG(TF("SetArgs -%s:%s"), *strKey, *strValue);

                m_cmdArgv.Add(std::move(strKey), std::move(strValue));
            }
        }
        pszArg = CXChar::Tok(nullptr, TF(" "), pszToken);
    }
}

void CServerConfig::SetArgs(Int nArgc, PXStr* ppszArgv)
{
    m_cmdArgv.RemoveAll();

    CString strKey;
    CString strValue;
    for (Int i = 1; i < nArgc; ++i)
    {
        PXStr pszArg = ppszArgv[i];
        if (pszArg[0] == TF('-'))
        {
            ++pszArg;
            PCXStr pszSep = CXChar::Chr(pszArg, TF(':'));
            if (pszSep != nullptr)
            {
                strKey.FillBuffer(pszArg, (pszSep - pszArg));
                strValue = (pszSep + 1);

                DEV_DEBUG(TF("SetArgs -%s:%s"), *strKey, *strValue);
                m_cmdArgv.Add(std::move(strKey), std::move(strValue));
            }
        }
    }
}

bool CServerConfig::Init(void)
{
    CPlatform::SetLocal(LC_ALL, TF("chs")); // 支持简体中文日志
    m_KeyValue.SetValue(ServerConfig, (void*)this);

    bool bRet = false;
    if (m_cmdArgv.GetSize() > 0)
    {
        PAIR_ARGV* pPair = m_cmdArgv.Find(ServerConfig);
        if (pPair != nullptr)
        {
            CString& strConfigName = pPair->m_V;
            if (strConfigName[0] == TF('\\'))
            {   // 配置信息以\开始的表示是程序目录的相对路径
                m_strConfigName = strConfigName.GetBuffer(1);
            }
            else if ((strConfigName[0] == TF('.')) && (strConfigName[1] == TF('/')))
            {   // 配置信息以./开始的表示是程序目录的相对路径
                m_strConfigName = strConfigName.GetBuffer(2);
            }
            else
            {
                m_strConfigName = strConfigName;
                bRet = true;
            }
        }
    }
    if (bRet == false)
    {
        m_FilePath.GetFullPath(CFilePath::ModulePath, m_strConfigName);
    }
    bRet = false;
    CFileReadStream Reader;
    if (Reader.Create(*m_strConfigName))
    {
        CXMLDocument Config;
        if (Config.Load(Reader))
        {
            CXMLNode* pRoot = Config.Find(ServerConfig);
            if (pRoot != nullptr)
            {
                bRet = LoadConfig(pRoot);
            }
            else
            {
                DEV_WARN(TF("读取配置文件[%s]失败!"), *m_strConfigName);
            }
        }
        else
        {
            DEV_WARN(TF("解析配置文件[%s]失败!"), *m_strConfigName);
        }
    }
    else
    {
        DEV_WARN(TF("加载配置文件[%s]失败!"), *m_strConfigName);
    }

    if (bRet)
    {
        CheckConfig();
        return true;
    }
    return false;
}

bool CServerConfig::Update(void)
{
    UpdateNetwork();
    UpdateCommon();
    UpdateServer();

    bool bRet = false;
    CFileReadStream Reader;
    if (Reader.Create(*m_strConfigName))
    {
        CXMLDocument Config;
        if (Config.Load(Reader))
        {
            CXMLNode* pRoot = Config.Find(ServerConfig);
            if (pRoot != nullptr)
            {
                bRet = LoadConfig(pRoot);
            }
        }
    }
    if (bRet)
    {
        CheckConfig();
        return true;
    }
    return false;
}

void CServerConfig::Exit(void)
{
    m_KeyValue.Remove(ServerConfig);
}

void CServerConfig::Dump(CFileLogPipe& Log, UInt uRank)
{
    if ((m_nLoadServers & COMMONT_MASK) == 0)
    {
        m_nLoadServers |= COMMONT_MASK;

        LOGV_DUMP(Log, TF("[Config]File=%s"), *m_strConfigName);

        if (m_cmdArgv.GetSize() > 0)
        {
            LOGV_DUMP(Log, TF("[Config]Args Count=%d"), m_cmdArgv.GetSize());
            Int nCount = 0;
            for (PINDEX index = m_cmdArgv.GetFirstIndex(); index != nullptr;)
            {
                PAIR_ARGV* pPair = m_cmdArgv.GetNext(index);
                LOGV_DUMP(Log, TF("[Config]Arg %d-th = %s:%s"), nCount, *pPair->m_K, *pPair->m_V);
                ++nCount;
            }
        }
        else
        {
            LOG_DUMP(Log, TF("[Config]Args is empty!"));
        }

        DumpNetwork(Log);
        DumpCommon(Log);
    }
    if (uRank == COMMONR_MASTER)
    {
        m_nLoadServers |= (1 << 16);
        DumpServer(Log, COMMONR_MASTER);
    }
    else if (uRank < COMMONR_MAX)
    {
        SERVER_CONFIG& SrvConfig = m_SrvConfig[uRank];
        for (UInt i = COMMONR_MASTER; i < COMMONR_MAX; ++i)
        {
            Int nLoadServer = (1 << (16 + i));
            if ((SrvConfig.Connect[i].usAttr != 0) && ((m_nLoadServers & nLoadServer) == 0))
            {
                m_nLoadServers |= nLoadServer;
                DumpServer(Log, i, true);
            }
        }
        DumpServer(Log, uRank);
    }
    Log.Flush();
}

bool CServerConfig::LoadConfig(CXMLNode* pRoot)
{
    LoadNetwork(pRoot);
    LoadCommon(pRoot);
    return LoadServer(pRoot);
}

bool CServerConfig::LoadNetwork(CXMLNode* pRoot)
{
    CXMLNode* pNetwork = pRoot->Find(NetworkConfig);
    if (pNetwork != nullptr)
    {
        LoadValue(pNetwork, NetworkAttr,    Value, m_nNetworkAttr);
        LoadValue(pNetwork, NetworkThread,  Value, m_nNetworkThread);
        LoadValue(pNetwork, NetworkAck,     Value, m_nNetworkAck);
        LoadValue(pNetwork, NetworkTimeout, Value, m_nNetworkTimeout);
        LoadValue(pNetwork, NetworkBuffer,  Value, m_nNetworkBuffer);
        LoadValue(pNetwork, NetworkJumbo,   Value, m_nNetworkJumbo);
    }
    return true;
}

bool CServerConfig::LoadCommon(CXMLNode* pRoot)
{
    CXMLNode* pCommon = pRoot->Find(CommonConfig);
    if (pCommon != nullptr)
    {
        LoadValue(pCommon, FrameLogLevel, Value, m_nFrameLogLevel);
        LoadValue(pCommon, MgrLogLevel,   Value, m_nMgrLogLevel);

        LoadValue(pCommon, UpdateTick,    Value, m_nUpdateTick);
        m_nUpdateTick = DEF::Max<Int>(m_nUpdateTick, TIMED_UPDATE_MIN);

        LoadValue(pCommon, PingEnable,    Value, m_nPingEnable);
        LoadValue(pCommon, AnyListen,     Value, m_nAnyListen);
        LoadValue(pCommon, FramePath,     Value, m_strFramePath);
        LoadValue(pCommon, RoutineFolder, Value, m_strRoutinePath);
        LoadValue(pCommon, LogFolder,     Value, m_strLogPath);

        CString strValue;
        LoadValue(pCommon, LoadServers, Value, strValue);
        m_nLoadServers = 0;
        if (strValue.Find(MasterServer) != -1)
        {
            m_nLoadServers |= COMMONT_MASTER;
        }
        if (strValue.Find(SlaveServer) != -1)
        {
            m_nLoadServers |= COMMONT_SLAVE;
        }
        if (strValue.Find(NodeServer) != -1)
        {
            m_nLoadServers |= COMMONT_NODE;
        }
    }
    return true;
}

bool CServerConfig::LoadServer(CXMLNode* pRoot)
{
    if ((m_nLoadServers & COMMONT_MASTER) != 0)
    {
        m_SrvConfig[COMMONR_MASTER].usType = (UShort)COMMONT_MASTER;
        CXMLNode* pMaster = pRoot->Find(MasterServer);
        if (pMaster != nullptr)
        {
            LoadRank(COMMONR_MASTER, pMaster);
        }
    }
    if ((m_nLoadServers & COMMONT_SLAVE) != 0)
    {
        CXMLNode* pSlave = pRoot->Find(SlaveServer);
        if (pSlave == nullptr)
        {
            DEV_WARN(TF("读取配置节点[%s]失败!"), SlaveServer);
            return false;
        }

        CXMLNode* pRank = pSlave->Find(Rank);
        while (pRank != nullptr)
        {
            UInt uRank = 0;
            if (LoadServerRank(pRank, uRank) == false)
            {
                DEV_WARN(TF("读取配置节点[%s-%s]的层级编号=%d失败!"), SlaveServer, Rank, uRank);
                return false;
            }
            m_SrvConfig[uRank].usType = (UShort)COMMONT_SLAVE;
            LoadRank(uRank, pRank);

            pRank = pSlave->Find(Rank, pRank);
        }
    }
    if ((m_nLoadServers & COMMONT_NODE) != 0)
    {
        CXMLNode* pNode = pRoot->Find(NodeServer);
        if (pNode == nullptr)
        {
            DEV_WARN(TF("读取配置节点[%s]失败!"), NodeServer);
            return false;
        }

        CXMLNode* pRank = pNode->Find(Rank);
        while (pRank != nullptr)
        {
            UInt uRank = 0;
            if (LoadServerRank(pRank, uRank) == false)
            {
                DEV_WARN(TF("读取配置节点[%s-%s]的层级编号=%d失败!"), NodeServer, Rank, uRank);
                return false;
            }
            m_SrvConfig[uRank].usType = (UShort)COMMONT_NODE;
            LoadRank(uRank, pRank);

            pRank = pNode->Find(Rank, pRank);
        }
    }
    return true;
}

bool CServerConfig::LoadRank(UInt uRank, CXMLNode* pRank)
{
    LoadServerAttr(pRank, m_SrvConfig[uRank]);
    LoadServerAddr(pRank, m_SrvConfig[uRank]);
    LoadServerRoutine(pRank, m_SrvConfig[uRank]);
    return true;
}

void CServerConfig::CheckConfig(void)
{
    CheckNetwork();
    CheckCommon();
    CheckServer();
    CheckPath();
    //----------------------------------
    for (UInt i = COMMONR_MASTER; i < COMMONR_MAX; ++i)
    {
        CheckAttr(i);
        CheckAddr(i);
        CheckRoutine(i);
    }
}

void CServerConfig::CheckNetwork(void)
{
    PAIR_ARGV* pPair = m_cmdArgv.Find(NetworkAttr);
    if (pPair != nullptr)
    {
        m_nNetworkAttr = pPair->m_V.ToInt();
    }
    pPair = m_cmdArgv.Find(NetworkThread);
    if (pPair != nullptr)
    {
        m_nNetworkThread = pPair->m_V.ToInt();
        m_nNetworkAttr  |= CNETTraits::ATTR_THREAD;
    }
    pPair = m_cmdArgv.Find(NetworkAck);
    if (pPair != nullptr)
    {
        m_nNetworkAck = pPair->m_V.ToInt();
        m_nNetworkAttr  |= CNETTraits::ATTR_ACK_TIME;
    }
    pPair = m_cmdArgv.Find(NetworkTimeout);
    if (pPair != nullptr)
    {
        m_nNetworkTimeout = pPair->m_V.ToInt();
        m_nNetworkAttr  |= CNETTraits::ATTR_TIMEOUT_TIME;
    }
    pPair = m_cmdArgv.Find(NetworkBuffer);
    if (pPair != nullptr)
    {
        m_nNetworkBuffer = pPair->m_V.ToInt();
        m_nNetworkAttr  |= CNETTraits::ATTR_MAX_BUFFER;
    }
    pPair = m_cmdArgv.Find(NetworkJumbo);
    if (pPair != nullptr)
    {
        m_nNetworkJumbo = pPair->m_V.ToInt();
        m_nNetworkAttr  |= CNETTraits::ATTR_MAX_JUMBOBUF;
    }
}

void CServerConfig::CheckCommon(void)
{
    PAIR_ARGV* pPair = m_cmdArgv.Find(FrameLogLevel);
    if (pPair != nullptr)
    {
        m_nFrameLogLevel = pPair->m_V.ToInt();
    }
    pPair = m_cmdArgv.Find(MgrLogLevel);
    if (pPair != nullptr)
    {
        m_nMgrLogLevel = pPair->m_V.ToInt();
    }
    pPair = m_cmdArgv.Find(UpdateTick);
    if (pPair != nullptr)
    {
        m_nUpdateTick = pPair->m_V.ToInt();
        m_nUpdateTick = DEF::Max<Int>(m_nUpdateTick, TIMED_UPDATE_MIN);
    }
    pPair = m_cmdArgv.Find(PingEnable);
    if (pPair != nullptr)
    {
        m_nPingEnable = pPair->m_V.ToInt();
    }
    pPair = m_cmdArgv.Find(AnyListen);
    if (pPair != nullptr)
    {
        m_nAnyListen = pPair->m_V.ToInt();
    }
    pPair = m_cmdArgv.Find(FramePath);
    if (pPair != nullptr)
    {
        m_strFramePath = pPair->m_V;
    }
    pPair = m_cmdArgv.Find(RoutineFolder);
    if (pPair != nullptr)
    {
        m_strRoutinePath = pPair->m_V;
    }
    pPair = m_cmdArgv.Find(LogFolder);
    if (pPair != nullptr)
    {
        m_strLogPath = pPair->m_V;
    }
    pPair = m_cmdArgv.Find(LoadServers);
    if (pPair != nullptr)
    {
        m_nLoadServers = 0;
        if (pPair->m_V.Find(MasterServer) != -1)
        {
            m_nLoadServers |= COMMONT_MASTER;
        }
        if (pPair->m_V.Find(SlaveServer) != -1)
        {
            m_nLoadServers |= COMMONT_SLAVE;
        }
        if (pPair->m_V.Find(NodeServer) != -1)
        {
            m_nLoadServers |= COMMONT_NODE;
        }
    }
}

void CServerConfig::CheckServer(void)
{
    CString strConfigServer;
    for (UInt i = COMMONR_MASTER; i < COMMONR_MAX; ++i)
    {
        strConfigServer.Format(TF("%s%d"), ConfigServer, i);
        PAIR_ARGV* pPair = m_cmdArgv.Find(strConfigServer);
        if (pPair != nullptr)
        {
            UShort usType = (UShort)pPair->m_V.ToULong();
            if (usType != 0)
            {
                switch (usType)
                {
                case COMMONT_MASTER:
                case COMMONT_SLAVE:
                case COMMONT_NODE:
                    {
                        m_SrvConfig[i].usType = usType;
                        UpdateServer(i, true);
                    }
                    break;
                default:{}
                }
            }
            else
            {
                m_SrvConfig[i].usType = COMMONT_NONE;
            }
        }
    }
}

void CServerConfig::CheckPath(void)
{
    if (m_strFramePath.IsEmpty())
    {
        m_FilePath.GetFullPath(CFilePath::ModulePath, m_strFramePath);
#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_LINUX)
        m_strFramePath += LibPrefix;
#endif
        m_strFramePath += Frame;
    }
    m_strFramePath += LibExt;

    m_strRoutinePath.TrimRight(TF('/'));
    m_strRoutinePath.TrimRight(TF('\\'));
    m_strLogPath.TrimRight(TF('/'));
    m_strLogPath.TrimRight(TF('\\'));
    Int nPos = m_strConfigName.Find(PathSepChar, 0, true);
    if (nPos != -1)
    {
        CString strRoutinePath = m_strConfigName.Left(nPos + 1);
        if (m_strRoutinePath.IsEmpty())
        {
            strRoutinePath  += RoutineFolder;
            m_strRoutinePath = strRoutinePath;
        }
        else if (m_strRoutinePath[0] == TF('\\'))
        {   // 配置信息以\开始的表示是程序目录的相对路径
            strRoutinePath  += m_strRoutinePath.GetBuffer(1);
            m_strRoutinePath = strRoutinePath;
        }
        else if ((m_strRoutinePath[0] == TF('.')) && (m_strRoutinePath[1] == TF('/')))
        {   // 配置信息以./开始的表示是程序目录的相对路径
            strRoutinePath  += m_strRoutinePath.GetBuffer(2);
            m_strRoutinePath = strRoutinePath;
        }
        m_FilePath.CreateFolder(m_strRoutinePath.GetBuffer());

        CString strLogPath = m_strConfigName.Left(nPos + 1);
        if (m_strLogPath.IsEmpty())
        {
            strLogPath  += LogFolder;
            m_strLogPath = strLogPath;
        }
        else if (m_strLogPath[0] == TF('\\'))
        {   // 配置信息以\开始的表示是程序目录的相对路径
            strLogPath  += m_strLogPath.GetBuffer(1);
            m_strLogPath = strLogPath;
        }
        else if ((m_strLogPath[0] == TF('.')) && (m_strLogPath[1] == TF('/')))
        {   // 配置信息以./开始的表示是程序目录的相对路径
            strLogPath  += m_strLogPath.GetBuffer(2);
            m_strLogPath = strLogPath;
        }
        m_FilePath.CreateFolder(m_strLogPath.GetBuffer());
    }
    else
    {
        if (m_strRoutinePath.IsEmpty())
        {
            m_strRoutinePath = RoutineFolder;
        }
        m_FilePath.CreateFolder(m_strRoutinePath.GetBuffer(), CFilePath::ModulePath);

        if (m_strLogPath.IsEmpty())
        {
            m_strLogPath = LogFolder;
        }
        m_FilePath.CreateFolder(m_strLogPath.GetBuffer(), CFilePath::ModulePath);
    }
    m_strRoutinePath += PathSepChar;
}

void CServerConfig::CheckAttr(UInt uRank)
{
    SERVER_CONFIG& SrvConfig = m_SrvConfig[uRank];
    if (SrvConfig.usType != (UShort)COMMONT_NONE)
    {
        if (SrvConfig.usType != (UShort)COMMONT_NODE)
        {
            // 2. Slave必须链接Master服务并且从Master同步服务信息
            SrvConfig.usSync = (UShort)COMMONR_MASTER;
        }
        else
        {
            if ((SrvConfig.usSync >= (UShort)COMMONR_MAX) || (SrvConfig.usSync == uRank))
            {
                SrvConfig.usSync = (UShort)COMMONR_MASTER;
            }
            if (m_SrvConfig[SrvConfig.usSync].usType == (UShort)COMMONT_NODE)
            {
                // 3. Node必须链接Master或者Slave服务
                SrvConfig.usSync = (UShort)COMMONR_MASTER;
            }
        }

        if (SrvConfig.usQueue == 0)
        {
            SrvConfig.usQueue = (UShort)CPlatform::CheckThread(UINT_MAX); // cpu cores
        }
        else
        {
            SrvConfig.usQueue = (UShort)CPlatform::CheckThread(SrvConfig.usQueue);
        }
        if (SrvConfig.strName.IsEmpty())
        {
            SrvConfig.strName.Format(TF("(%s-%d)"), Rank, uRank);
        }
        if (SrvConfig.strInst.IsEmpty())
        {
            SrvConfig.strInst.Format(TF("%s-%d"), *SrvConfig.strRoutine, SrvConfig.usId);
        }
    }
}

void CServerConfig::CheckAddr(UInt uRank)
{
    SERVER_CONFIG& SrvConfig = m_SrvConfig[uRank];
    if (SrvConfig.usType != (UShort)COMMONT_NONE)
    {
        UShort usAttr = (m_nNetworkAttr & CNETTraits::ATTR_IPV6) ? (UShort)CNETTraits::ATTR_IPV6 : (UShort)CNETTraits::ATTR_IPV4;

        if ((SrvConfig.usType == (UShort)COMMONT_MASTER) || (SrvConfig.usType == (UShort)COMMONT_SLAVE))
        {
            SrvConfig.Listen.usAttr = usAttr;
            if (SrvConfig.Listen.usPort == 0)
            {
                SrvConfig.Listen.usPort = (UShort)(LISTENP_DEFAULT + uRank);
            }
        }
        if (SrvConfig.usType == (UShort)COMMONT_SLAVE)
        {
            // 2. Slave必须链接Master服务并且从Master同步服务信息
            SrvConfig.Connect[COMMONR_MASTER].usAttr = usAttr;
        }
        else if (SrvConfig.usType == (UShort)COMMONT_NODE)
        {
            // 3. Node必须链接Master或者Slave服务
            SrvConfig.Connect[SrvConfig.usSync].usAttr = usAttr;
        }
        if (SrvConfig.Listen.strIp.IsEmpty())
        {
            SrvConfig.Listen.strIp = LocalHost;
        }
    }
}

void CServerConfig::CheckRoutine(UInt uRank)
{
    SERVER_CONFIG& SrvConfig = m_SrvConfig[uRank];
    if (SrvConfig.strModule.IsEmpty() == false)
    {
        CheckModule(SrvConfig.strModule);
    }
}

void CServerConfig::UpdateNetwork(void)
{
    m_nNetworkAttr    = 0;
    m_nNetworkThread  = 0;
    m_nNetworkAck     = 0;
    m_nNetworkTimeout = 0;
    m_nNetworkBuffer  = 0;
    m_nNetworkJumbo   = 0;
}

void CServerConfig::UpdateCommon(void)
{
    m_nUpdateTick  = TIMED_UPDATE_DEF;
    m_nPingEnable  = FALSE;
    m_nAnyListen   = TRUE;
    m_nLoadServers = COMMONT_ALL;
    m_strFramePath.Empty();
    m_strRoutinePath.Empty();
    m_strLogPath.Empty();
}

void CServerConfig::UpdateServer(void)
{
    for (UInt i = COMMONR_MASTER; i < COMMONR_MAX; ++i)
    {
        UpdateServer(i);
    }
}

void CServerConfig::UpdateServer(UInt uRank, bool bArgv)
{
    SERVER_CONFIG& SrvConfig = m_SrvConfig[uRank];
    if (bArgv == false)
    {
        SrvConfig.usType  = (UShort)COMMONT_NONE;
        SrvConfig.usSync  = (UShort)COMMONR_MASTER;
        SrvConfig.usId    = (UShort)(uRank * LOADI_RADIX);
        SrvConfig.usQueue = 0;
        SrvConfig.usLoad  = 0;
        SrvConfig.usRate  = (UShort)COMMONC_RATE_LOAD;
        SrvConfig.strName.Empty();
        SrvConfig.strRoutine.Empty();
        SrvConfig.strInst.Empty();
        SrvConfig.strModule.Empty();
        SrvConfig.Listen.Reset();
        for (UInt i = COMMONR_MASTER; i < COMMONR_MAX; ++i)
        {
            SrvConfig.Connect[i].Reset();
        }
    }
    else
    {
        LoadServerAttr(uRank, SrvConfig);
        LoadServerAddr(uRank, SrvConfig);
        LoadServerRoutine(uRank, SrvConfig);
    }
}

void CServerConfig::DumpNetwork(CFileLogPipe& Log)
{
    LOGV_DUMP(Log, TF("[Config][%s]%s=%d, %s=%d, %s=%d, %s=%d, %s=%d, %s=%d"),
              NetworkConfig, 
              NetworkAttr,    m_nNetworkAttr, 
              NetworkThread,  m_nNetworkThread,
              NetworkAck,     m_nNetworkAck,
              NetworkTimeout, m_nNetworkTimeout,
              NetworkBuffer,  m_nNetworkBuffer,
              NetworkJumbo,   m_nNetworkJumbo);
}

void CServerConfig::DumpCommon(CFileLogPipe& Log)
{
    LOGV_DUMP(Log, TF("[Config][%s]%s=%d[S], %s=%d, %s=%d, %s=%X"),
              CommonConfig,
              UpdateTick,  m_nUpdateTick,
              PingEnable,  m_nPingEnable,
              AnyListen,   m_nAnyListen,
              LoadServers, (m_nLoadServers & COMMONT_ALL));
}

void CServerConfig::DumpServer(CFileLogPipe& Log, UInt uRank, bool bListenOnly)
{
    SERVER_CONFIG& SrvConfig = m_SrvConfig[uRank];
    if (bListenOnly == false)
    {
        LOGV_DUMP(Log, TF("[Config][Type=%d---Name=%s]%s=%d, %s=%d, %s=%d, %s=%d, %s=%d"),
                  SrvConfig.usType,
                  SrvConfig.strName.GetBuffer(),
                  Id,      SrvConfig.usId,
                  Sync,    SrvConfig.usSync,
                  Queue,   SrvConfig.usQueue,
                  Load,    SrvConfig.usLoad,
                  Rate,    SrvConfig.usRate);

        LOGV_DUMP(Log, TF("[Config][Type=%d---Name=%s]%s=%s, %s=%s, %s=%s"),
                  SrvConfig.usType,
                  SrvConfig.strName.GetBuffer(),
                  Routine, SrvConfig.strRoutine.GetBuffer(),
                  Inst,    SrvConfig.strInst.GetBuffer(),
                  Module,  SrvConfig.strModule.GetBuffer());

        if (SrvConfig.Listen.usAttr != 0)
        {
            LOGV_DUMP(Log, TF("[Config][Type=%d---Name=%s]%s=%s[%d]"),
                      SrvConfig.usType,
                      SrvConfig.strName.GetBuffer(),
                      Listen,
                      SrvConfig.Listen.strIp.GetBuffer(),
                      SrvConfig.Listen.usPort);
        }

        for (UInt i = COMMONR_MASTER; i < COMMONR_MAX; ++i)
        {
            if (SrvConfig.Connect[i].usAttr != 0)
            {
                LOGV_DUMP(Log, TF("[Config][Type=%d---Name=%s]%s to rank=%d, local bind=%s[%d]"),
                          SrvConfig.usType,
                          SrvConfig.strName.GetBuffer(),
                          Connect, i,
                          SrvConfig.Connect[i].strIp.GetBuffer(),
                          SrvConfig.Connect[i].usPort);
            }
        }
    }
    else if (SrvConfig.Listen.usAttr != 0)
    {
        LOGV_DUMP(Log, TF("[Config][Connect to listen rank=%d]%s=%s[%d]"),
                  uRank,
                  Listen,
                  SrvConfig.Listen.strIp.GetBuffer(),
                  SrvConfig.Listen.usPort);
    }
}

bool CServerConfig::LoadServerRank(CXMLNode* pNode, UInt& uRank)
{
    // 1. SlaveServer和NodeServer里的节点必须有层级编号, 所有编号必须唯一不可重复
    CXMLElement* pElement = pNode->To<CXMLElement, CXMLTraits::XMLT_ELEMENT>();
    if (pElement != nullptr)
    {
        pElement->GetAttribute(Value, uRank);
        if ((uRank > COMMONR_MASTER) && (uRank < COMMONR_MAX))
        {
            return (m_SrvConfig[uRank].usType == (UShort)COMMONT_NONE);
        }
    }
    return false;
}

bool CServerConfig::LoadServerAttr(CXMLNode* pNode, SERVER_CONFIG& SrvConfig)
{
    CXMLElement* pElement = pNode->To<CXMLElement, CXMLTraits::XMLT_ELEMENT>();
    if (pElement != nullptr)
    {
        pElement->GetAttribute(Name,  SrvConfig.strName);
        pElement->GetAttribute(Sync,  SrvConfig.usSync);
        pElement->GetAttribute(Id,    SrvConfig.usId);
        pElement->GetAttribute(Queue, SrvConfig.usQueue);
        pElement->GetAttribute(Load,  SrvConfig.usLoad);
        pElement->GetAttribute(Rate,  SrvConfig.usRate);
        return true;
    }
    return false;
}

bool CServerConfig::LoadServerAttr(UInt uRank, SERVER_CONFIG& SrvConfig)
{
    CString strRank;
    strRank.ToString(uRank);

    CString strKey = Name;
    strKey += strRank;
    PAIR_ARGV* pPair = m_cmdArgv.Find(strKey);
    if (pPair != nullptr)
    {
        SrvConfig.strName = pPair->m_V;
    }

    strKey  = Sync;
    strKey += strRank;
    pPair = m_cmdArgv.Find(strKey);
    if (pPair != nullptr)
    {
        SrvConfig.usSync = (UShort)pPair->m_V.ToInt();
    }

    strKey = Id;
    strKey += strRank;
    pPair = m_cmdArgv.Find(strKey);
    if (pPair != nullptr)
    {
        SrvConfig.usId = (UShort)pPair->m_V.ToInt();
    }

    strKey = Queue;
    strKey += strRank;
    pPair = m_cmdArgv.Find(strKey);
    if (pPair != nullptr)
    {
        SrvConfig.usQueue = (UShort)pPair->m_V.ToInt();
    }

    strKey = Load;
    strKey += strRank;
    pPair = m_cmdArgv.Find(strKey);
    if (pPair != nullptr)
    {
        SrvConfig.usLoad = (UShort)pPair->m_V.ToInt();
    }

    strKey = Rate;
    strKey += strRank;
    pPair = m_cmdArgv.Find(strKey);
    if (pPair != nullptr)
    {
        SrvConfig.usRate = (UShort)pPair->m_V.ToInt();
    }
    return true;
}

bool CServerConfig::LoadServerAddr(CXMLNode* pNode, SERVER_CONFIG& SrvConfig)
{
    UShort usAttr = (m_nNetworkAttr & CNETTraits::ATTR_IPV6) ? (UShort)CNETTraits::ATTR_IPV6 : (UShort)CNETTraits::ATTR_IPV4;
    
    CXMLNode* pListen = pNode->Find(Listen);
    if (pListen != nullptr)
    {
        SrvConfig.Listen.usAttr = usAttr;
        LoadValue(pListen, Ip, SrvConfig.Listen.strIp);
        LoadValue(pListen, Port, SrvConfig.Listen.usPort);
    }
    CXMLNode* pConnect = pNode->Find(Connect);
    while (pConnect != nullptr)
    {
        UShort usRank = 0;
        LoadValue(pConnect, Value, usRank);
        if (usRank < (UShort)COMMONR_MAX)
        {
            SrvConfig.Connect[usRank].usAttr = usAttr;
            LoadValue(pConnect, Ip, SrvConfig.Connect[usRank].strIp);
            LoadValue(pConnect, Port, SrvConfig.Connect[usRank].usPort);
            if (m_SrvConfig[usRank].Listen.usAttr == 0)
            {
                m_SrvConfig[usRank].Listen.usAttr = usAttr;
                LoadValue(pConnect, ListenIp, m_SrvConfig[usRank].Listen.strIp);
                LoadValue(pConnect, ListenPort, m_SrvConfig[usRank].Listen.usPort);
            }
        }
        pConnect = pNode->Find(Connect, pConnect);
    }
    return true;
}

bool CServerConfig::LoadServerAddr(UInt uRank, SERVER_CONFIG& SrvConfig)
{
    UShort usAttr = (m_nNetworkAttr & CNETTraits::ATTR_IPV6) ? (UShort)CNETTraits::ATTR_IPV6 : (UShort)CNETTraits::ATTR_IPV4;

    CString strRank;
    strRank.ToString(uRank);

    CString strKey = Listen;
    strKey += strRank;
    PAIR_ARGV* pPair = m_cmdArgv.Find(strKey);
    if (pPair != nullptr)
    {
        SrvConfig.Listen.usAttr = usAttr;

        size_t stIndex = 0;
        SrvConfig.Listen.usPort = (UShort)pPair->m_V.ToULong(stIndex);
        if (stIndex != (size_t)-1)
        {
            ++stIndex; // ,
            SrvConfig.Listen.strIp = pPair->m_V.RightPos(stIndex);
        }
    }

    strKey  = Connect;
    strKey += strRank;
    pPair = m_cmdArgv.Find(strKey);
    if (pPair != nullptr)
    {
        CString strConnect = pPair->m_V;
        PXStr pszToken = nullptr;
        PXStr pszArg   = CXChar::Tok(*strConnect, TF(";"), pszToken);
        while (pszArg != nullptr)
        {
            PXStr pszEnd = nullptr;
            uRank = (UInt)CXChar::ToULong(pszArg, &pszEnd);
            if (uRank < COMMONR_MAX)
            {
                SrvConfig.Connect[uRank].usAttr = usAttr;
                if (pszEnd != nullptr)
                {
                    ++pszEnd; // ,
                    m_SrvConfig[uRank].Listen.usAttr = usAttr;
                    m_SrvConfig[uRank].Listen.usPort = (UShort)CXChar::ToULong(pszEnd, &pszEnd);
                }
                if (pszEnd != nullptr)
                {
                    ++pszEnd; // ,
                    PXStr p = (PXStr)CXChar::Chr(pszEnd, TF(','));
                    if (p != nullptr)
                    {
                        m_SrvConfig[uRank].Listen.strIp.FillBuffer(pszEnd, (p - pszEnd));
                    }
                    else
                    {
                        m_SrvConfig[uRank].Listen.strIp = pszEnd;
                    }
                    pszEnd = p;
                }
                if (pszEnd != nullptr)
                {
                    ++pszEnd; // ,
                    SrvConfig.Connect[uRank].usPort = (UShort)CXChar::ToULong(pszEnd, &pszEnd);
                }
                if (pszEnd != nullptr)
                {
                    ++pszEnd; // ,
                    SrvConfig.Listen.strIp = pszEnd;
                }
            }
            pszArg = CXChar::Tok(nullptr, TF(";"), pszToken);
        }
    }
    return true;
}

bool CServerConfig::LoadServerRoutine(CXMLNode* pNode, SERVER_CONFIG& SrvConfig)
{
    LoadValue(pNode, Routine, Value,  SrvConfig.strRoutine);
    LoadValue(pNode, Routine, Inst,   SrvConfig.strInst);
    LoadValue(pNode, Routine, Module, SrvConfig.strModule);
    return true;
}

bool CServerConfig::LoadServerRoutine(UInt uRank, SERVER_CONFIG& SrvConfig)
{
    CString strRank;
    strRank.ToString(uRank);

    CString strKey = Routine;
    strKey += strRank;
    PAIR_ARGV* pPair = m_cmdArgv.Find(strKey);
    if (pPair != nullptr)
    {
        SrvConfig.strRoutine = pPair->m_V;
    }

    strKey  = Inst;
    strKey += strRank;
    pPair = m_cmdArgv.Find(strKey);
    if (pPair != nullptr)
    {
        SrvConfig.strInst = pPair->m_V;
    }

    strKey  = Module;
    strKey += strRank;
    pPair = m_cmdArgv.Find(strKey);
    if (pPair != nullptr)
    {
        SrvConfig.strModule = pPair->m_V;
    }
    return true;
}

bool CServerConfig::LoadValue(CXMLNode* pNode, PCXStr pszAttr, Int& nValue)
{
    CXMLElement* pElement = pNode->To<CXMLElement, CXMLTraits::XMLT_ELEMENT>();
    if (pElement != nullptr)
    {
        return pElement->GetAttribute(pszAttr, nValue);
    }
    return false;
}

bool CServerConfig::LoadValue(CXMLNode* pNode, PCXStr pszElement, PCXStr pszAttr, Int& nValue)
{
    CXMLNode* pElement = pNode->Find(pszElement);
    if (pElement != nullptr)
    {
        return LoadValue(pElement, pszAttr, nValue);
    }
    return false;
}

bool CServerConfig::LoadValue(CXMLNode* pNode, PCXStr pszAttr, UShort& usValue)
{
    CXMLElement* pElement = pNode->To<CXMLElement, CXMLTraits::XMLT_ELEMENT>();
    if (pElement != nullptr)
    {
        return pElement->GetAttribute(pszAttr, usValue);
    }
    return false;
}

bool CServerConfig::LoadValue(CXMLNode* pNode, PCXStr pszElement, PCXStr pszAttr, UShort& usValue)
{
    CXMLNode* pElement = pNode->Find(pszElement);
    if (pElement != nullptr)
    {
        return LoadValue(pElement, pszAttr, usValue);
    }
    return false;
}

bool CServerConfig::LoadValue(CXMLNode* pNode, PCXStr pszAttr, CString& strValue)
{
    CXMLElement* pElement = pNode->To<CXMLElement, CXMLTraits::XMLT_ELEMENT>();

    return pElement->GetAttribute(pszAttr, strValue);
}

bool CServerConfig::LoadValue(CXMLNode* pNode, PCXStr pszElement, PCXStr pszAttr, CString& strValue)
{
    CXMLNode* pElement = pNode->Find(pszElement);
    if (pElement != nullptr)
    {
        return LoadValue(pElement, pszAttr, strValue);
    }
    return false;
}

